#include "CLUT.h"

////////////////////////////////////////////////////////////////////////////////
//Fonction de chargement d'une Color LookUp Table
////////////////////////////////////////////////////////////////////////////////
bool loadCLUT(QString filename, cv::Mat & clut)
{
	QFile file(filename);
	
	if(file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		QTextStream in(&file);
		QString line;
		
		line=in.readLine();
		if(line!=QString("CLUT"))return false;

		line=in.readLine();
		int res=line.toInt();

		int i=0;		
		clut.create(res*res*res,1,CV_8UC1);
		while(!in.atEnd())
		{
			line=in.readLine();
			clut.data[i]=line.toInt();			
			i++;
		}
		file.close();
		return true;
	}
	else
	{
		return false;
	}
}

////////////////////////////////////////////////////////////////////////////////
//Fonction de sauvegarde d'une CLUT
////////////////////////////////////////////////////////////////////////////////
void saveCLUT(QString filename, cv::Mat & clut)
{
	QFile file(filename);
	
	if(file.open(QIODevice::WriteOnly | QIODevice::Text))
	{
		QTextStream out(&file);
		QString line;
		
		int res=(int)round(pow(clut.rows,1.0/3.0));
		out<<"CLUT\n"<<res<<"\n";

		for(int i=0;i<clut.rows;i++)
		{
			out<<clut.data[i]<<"\n";
		}
		file.close();
	}
}

////////////////////////////////////////////////////////////////////////////////
//Fonction de chargement d'une Color LookUp Table de type Fuzzy
////////////////////////////////////////////////////////////////////////////////
bool loadFuzzyCLUT(QString filename, cv::Mat & fuzzyclut)
{
	QFile file(filename);
	
	if(file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		QTextStream in(&file);
		QString line;
		
		//
		line=in.readLine();
		if(line!=QString("FUZZY_CLUT"))return false;

		//On lit la premiere ligne qui est la resolution de la lookup table
		line=in.readLine();
		int res=line.toInt();
		
		//On lit la seconde ligne qui est la profondeur de la lookup table
		line=in.readLine();
		int depth=line.toInt();

		//On alloue la CLUT
		fuzzyclut.create(res*res*res,depth,CV_32FC1);

		//On affecte les valeurs du fichier
		int i=0;
		while(!in.atEnd())
		{
			line=in.readLine();
			QStringList lineSplited=line.split(',',QString::SkipEmptyParts);
			if(lineSplited.size()==depth && i<res*res*res)
			{				
				for(int j=0;j<depth;j++)
				{
					((float*)fuzzyclut.data)[i*depth+j]=lineSplited[j].toFloat();
				}
				i++;
			}
			else
			{
				printf("Erreur1 : Le fichier est corrompu ligne=%d\n",i);
			}
		}
		
		//Petite verification
		if(i!=res*res*res)
		{
			printf("Erreur2 : Le fichier est corrompu %d %d\n",i,res*res*res);
		}
		file.close();
		return true;
	}		
	else
	{
		return false;
	}
}

////////////////////////////////////////////////////////////////////////////////
//Fonction de sauvegarde d'une CLUT de type Fuzzy
////////////////////////////////////////////////////////////////////////////////
void saveFuzzyCLUT(QString filename, cv::Mat & fuzzyclut)
{
	if(!fuzzyclut.empty())
	{
		if(fuzzyclut.depth()==CV_32F && fuzzyclut.channels()==1)
		{
				QFile file(filename);
				if(file.open(QIODevice::WriteOnly | QIODevice::Text))
				{
					QTextStream out(&file);
					QString line;
				
					//On recupere la resolution puis on l'ecrit en 1ere ligne
					int res=(int)round(pow(fuzzyclut.rows,1.0/3.0));
					out<<"FUZZY_CLUT\n"<<res<<"\n";

					//On ecrit ensuite la profondeur de la CLUT
					out<<fuzzyclut.cols<<"\n";

					//On enregistre les valeurs de la CLUT
					for(int i=0;i<fuzzyclut.rows;i++)
					{
						for(int j=0;j<fuzzyclut.cols;j++)
						{
							out<<((float*)fuzzyclut.data)[i*fuzzyclut.cols+j]<<",";
						}
						out<<"\n";
					}

					file.close();
				}
		}
		else
		{
			printf("Error : Bad input must be CV_32FC1\n");
		}
	}
} 
////////////////////////////////////////////////////////////////////////////////
//Creation d'une FUZZY_CLUT par chargement de donnees d'enquettes;
////////////////////////////////////////////////////////////////////////////////
bool createFuzzyCLUT(QString filename, int res, int depth, cv::Mat & fuzzyclut);
{
	QFile file(filename);
	
	if(file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		QTextStream in(&file);
		QString line;
		
		fuzzyclut.create(res*res*res,depth,CV_32FC1);
		fuzzyclut.setTo(0.0);
		
		int res2=res*res;
		
		while(!in.atEnd())
		{
			line=in.readLine();
			QStringList line_splited=line.split(";");
			if(line_splited.size()==4)
			{
				uchar R=line_splited[0].toInt();
				uchar G=line_splited[1].toInt();
				uchar B=line_splited[2].toInt();
				uchar C=line_splited[3].toInt();
				
				((float*)fuzzyclut.data)[fuzzyclut.cols*(R*res2+G*res+B)+C]+=1.0;
			}
		}
		
		normalize(fuzzyclut);
		
		file.close();
		return true;
	}
	else
	{
		return false;
	}
}

////////////////////////////////////////////////////////////////////////////////
//Normalisation d'une FUZZY_CLUT
////////////////////////////////////////////////////////////////////////////////
void normalize(cv::Mat & fuzzyclut)
{
	for(int i=0;i<fuzzyclut.rows;i++)
	{
		float n=0.0;
		for(int j=0;j<fuzzyclut.cols;j++)
		{
			n+=((float*)fuzzyclut.data)[i*fuzzyclut.cols+j];
		}
		if(n!=0)
		{
			for(int j=0;j<fuzzyclut.cols;j++)
			{
				((float*)fuzzyclut.data)[i*fuzzyclut.cols+j]/=n;
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//Conversion d'une image RGB en composantes Fuzzy en fonction de la Fuzzy CLUT
////////////////////////////////////////////////////////////////////////////////
void RGB_2_Fuzzy(cv::Mat & src, cv::Mat & fuzzyclut, cv::Mat & des)
{
	if(src.depth()==CV_8U && src.channels()==3)
	{
		//on alloue la matrice de sortie multi channels
		des.create(src.rows,src.cols,CV_32FC(fuzzyclut.cols));		

		//on recupere la resolution de la CLUT
		int res=(int)round(pow(fuzzyclut.rows,1.0/3.0));
		int res2=res*res;
		
		//on converti en utilisant la CLUT
		int np=src.rows*src.cols;
		for(int p=0;p<np;p++)
		{
				int r=floor(src.data[3*p]*res/256);
				int g=floor(src.data[3*p+1]*res/256);
				int b=floor(src.data[3*p+2]*res/256);

				//printf("%d %d %d %d\n",p,r,g,b);
				
				for(int i=0;i<fuzzyclut.cols;i++)
				{
					((float*)des.data)[fuzzyclut.cols*p+i]=((float*)fuzzyclut.data)[fuzzyclut.cols*(r*res2+g*res+b)+i];
				}
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC3\n");
	}
}

////////////////////////////////////////////////////////////////////////////////
//Retourne une image avec les composantes maximums de l'image source de type Fuzzy (voir fonction precedente)
////////////////////////////////////////////////////////////////////////////////
void maxFuzzy(cv::Mat & src, cv::Mat & des)
{
	if(src.depth()==CV_32F)
	{	
		des.create(src.rows,src.cols,CV_8UC1);
		int np=src.rows*src.cols;
		for(int p=0;p<np;p++)
		{		
			float max=0.0;
			int n=0;
			for(int i=0;i<src.channels();i++)
			{
				if(((float*)src.data)[src.channels()*p+i]>max)
				{
					max=((float*)src.data)[src.channels()*p+i];
					n=i;			
				}
			}
			des.data[p]=n;
		}
	}
	else
	{
		printf("Error : Bad input must be CV_32FC(n)\n");
	}
}

////////////////////////////////////////////////////////////////////////////////
//Construit une CLUT a partir des max d'une FuzzyCLUT
////////////////////////////////////////////////////////////////////////////////
void maxFuzzyCLUT_2_CLUT(cv::Mat & fuzzyclut,cv::Mat & clut)
{
	clut.create(fuzzyclut.rows,1,CV_8UC1);
	for(int p=0;p<fuzzyclut.rows;p++)
	{		
		float max=0.0;
		int n=0;
		for(int i=0;i<fuzzyclut.cols;i++)
		{
			if(((float*)fuzzyclut.data)[fuzzyclut.cols*p+i]>max)
			{
				max=((float*)fuzzyclut.data)[fuzzyclut.cols*p+i];
				n=i;			
			}
		}
		clut.data[p]=n;
	}
}

////////////////////////////////////////////////////////////////////////////////
//Construit une CLUT a partir de l'entropie d'une CLUT Fuzzy
////////////////////////////////////////////////////////////////////////////////
void entropyFuzzyCLUT_2_CLUT(cv::Mat & fuzzyclut, cv::Mat & clut)
{
	clut.create(fuzzyclut.rows,1,CV_8UC1);
	for(int n=0;n<fuzzyclut.rows;n++)
	{		
		float entropy=0.0;	
		float entropy_max=log2(fuzzyclut.cols);
		float p=0.0;
		for(int i=0;i<fuzzyclut.cols;i++)
		{
				p=(((float*)fuzzyclut.data)[fuzzyclut.cols*n+i]);
				if(p>0){
				entropy+=-p*log2(p);
				}
		}
		clut.data[n]=(unsigned char)(entropy/entropy_max*255);
		//printf("%f %d\n",entropy,clut.data[n]);
	}
}

////////////////////////////////////////////////////////////////////////////////
//Applique une clut le résultat est en NVG
////////////////////////////////////////////////////////////////////////////////
void RGB_2_GRAY(cv::Mat & src, cv::Mat & clut, cv::Mat & des)
{
	if(src.depth()==CV_8U && src.channels()==3)
	{
		des.create(src.rows,src.cols,CV_8UC1);
		
		//on recupere la resolution de la CLUT
		int res=(int)round(pow(clut.rows,1.0/3.0));
		int res2=res*res;

		int np=src.rows*src.cols;
		for(int p=0;p<np;p++)
		{
			int r=floor(src.data[3*p]*res/256);
			int g=floor(src.data[3*p+1]*res/256);
			int b=floor(src.data[3*p+2]*res/256);

			des.data[p]=clut.data[(r*res2+g*res+b)];
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC3\n");
	}
}
////////////////////////////////////////////////////////////////////////////////
//Retourne la valeur de la CLUT a rgb
////////////////////////////////////////////////////////////////////////////////
uchar CLUT(cv::Mat & clut,uchar r,uchar g,uchar b)
{
		int res=(int)round(pow(clut.rows,1.0/3.0));
		int res2=res*res;
		
		int R=floor(r*res/256);
		int G=floor(g*res/256);
		int B=floor(b*res/256);

		return clut.data[(R*res2+G*res+B)];
}
////////////////////////////////////////////////////////////////////////////////
//Retourne la valeur de la FUZZY_CLUT a r g b classe
////////////////////////////////////////////////////////////////////////////////
float FUZZY_CLUT(cv::Mat & fuzzyclut,uchar r,uchar g,uchar b,uchar classe)
{
	int res=(int)round(pow(fuzzyclut.rows,1.0/3.0));
	int res2=res*res;
	
	int R=floor(r*res/256);
	int G=floor(g*res/256);
	int B=floor(b*res/256);
	
	return ((float*)fuzzyclut.data)[fuzzyclut.cols*(R*res2+G*res+B)+classe];
}
////////////////////////////////////////////////////////////////////////////////
//Modification des poids dune couleurs celaon un pas step 
////////////////////////////////////////////////////////////////////////////////
void modifyWeightsFuzzyCLUT(cv::Mat & fuzzyclut,float step,uchar r,uchar g,uchar b,uchar C)
{
	int res=(int)round(pow(fuzzyclut.rows,1.0/3.0));
	int res2=res*res;
	
	int R=floor(r*res/256);
	int G=floor(g*res/256);
	int B=floor(b*res/256);
	
	float sstep=step/(fuzzyclut.cols-1);
	
	for(int i=0;i<fuzzyclut.cols;i++)
	{
		float * ptr=&(((float*)fuzzyclut.data)[fuzzyclut.cols*(R*res2+G*res+B)+i]);
		if(i!=C)
		{
			*ptr+=sstep;
		}
		else
		{
			*ptr-=step;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//Fonctions de gestion d'une conversion C11
////////////////////////////////////////////////////////////////////////////////
void RGB_2_C11(cv::Mat & src, cv::Mat & clut11c, cv::Mat & des)
{
	if(src.depth()==CV_8U && src.channels()==3)
	{
		des.create(src.rows,src.cols,CV_8UC1);
		
		//on recupere la resolution de la CLUT
		int res=(int)round(pow(clut11c.rows,1.0/3.0));
		int res2=res*res;

		int np=src.rows*src.cols;
		for(int p=0;p<np;p++)
		{
			int r=(int)floor(src.data[3*p]*res/256.0);
			int g=(int)floor(src.data[3*p+1]*res/256.0);
			int b=(int)floor(src.data[3*p+2]*res/256.0);

			des.data[p]=clut11c.data[(r*res2+g*res+b)];
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC3\n");
	}
}

void C11_2_RGB(cv::Mat & src, cv::Mat & des)
{
	unsigned char CLUT_COLORS_11[11][3]=
	{
		{0  ,0  ,0  },//noir		
		{128,128,128},//gris		
		{255,255,255},//blanc
		{255,0  ,0  },//rouge
		{255,128,0  },//orange		
		{255,255,0  },//jaune
		{0  ,255,0  },//vert
		{0  ,0  ,255},//bleu
		{150,0  ,0 },//pourpre
		{255,0  ,255},//rose 
		{128,64 ,0  }//maron 
	};

	if(src.depth()==CV_8U && src.channels()==1)
	{
		des.create(src.rows,src.cols,CV_8UC3);

		int np=src.rows*src.cols;
		for(int p=0;p<np;p++)
		{
			int n=src.data[p];
			if(n>=0 && n<11){
			des.data[3*p]=CLUT_COLORS_11[n][0];
			des.data[3*p+1]=CLUT_COLORS_11[n][1];
			des.data[3*p+2]=CLUT_COLORS_11[n][2];}
			else{printf("Error : Bad input %d\n",n);break;}
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC1\n");
	}
}
void RGB_2_RGB11(cv::Mat & src, cv::Mat & clut11c, cv::Mat & des)
{
	unsigned char CLUT_COLORS_11[11][3]=
	{
		{0  ,0  ,0  },//noir		
		{128,128,128},//gris		
		{255,255,255},//blanc
		{255,0  ,0  },//rouge
		{255,128,0  },//orange		
		{255,255,0  },//jaune
		{0  ,255,0  },//vert
		{0  ,0  ,255},//bleu
		{150,0  ,0  },//pourpre
		{255,0  ,255},//rose 
		{128,64 ,0  }//maron 
	};

	if(src.depth()==CV_8U && src.channels()==3)
	{
		des.create(src.rows,src.cols,CV_8UC3);

		//on recupere la resolution de la CLUT
		int res=(int)round(pow(clut11c.rows,1.0/3.0));
		int res2=res*res;
		
		int np=src.rows*src.cols*3;
		for(int p=0;p<np;p+=3)
		{
		
			
			int r=(int)floor(src.data[p]*res/256.0);
			int g=(int)floor(src.data[p+1]*res/256.0);
			int b=(int)floor(src.data[p+2]*res/256.0);

			int n=clut11c.data[ r*res2+g*res+b ];

			//if(r==g && g==b && (b==13 || b==14 || b==12 || b==11))printf("%d\n",n);	

			if(n>=0 && n<11){
			des.data[p]=CLUT_COLORS_11[n][0];
			des.data[p+1]=CLUT_COLORS_11[n][1];
			des.data[p+2]=CLUT_COLORS_11[n][2];
			}else{printf("Error : Bad input %d\n",n);break;}
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC3\n");
	}
}

////////////////////////////////////////////////////////////////////////////////
//Fonction qui retourne un histogramme ponderé par l'entropie
////////////////////////////////////////////////////////////////////////////////
void getEntropyHistRegionC11(cv::Mat & src, cv::Mat & entropy_src, cv::Mat & region_mask, cv::Mat & entropy_hist)
{
	if(src.depth()==CV_8U && src.channels()==1)
	{	
		if(region_mask.rows==src.rows && region_mask.cols==src.cols && region_mask.channels()==1 && region_mask.depth()==CV_8U)
		{
			entropy_hist.create(11,1,CV_32FC1);
			entropy_hist.setTo(cv::Scalar(0));
		
			int n=0;
			int np=src.rows*src.cols;
			for(int p=0;p<np;p++)
			{	
				if(region_mask.data[p]>0)
				{
					n++;
					((float*)entropy_hist.data)[src.data[p]]+=(float)entropy_src.data[p]/255.0;
				}
			}
			entropy_hist=entropy_hist/n;
		}
		else
		{
			printf("Error : Bad input mask\n");
		}
	}
	else
	{
		printf("Error : Bad input must be CV_32FC1\n");
	}
}

////////////////////////////////////////////////////////////////////////////////
//Fonction qui retourne une liste ordonnée des principales couleurs
////////////////////////////////////////////////////////////////////////////////
QStringList getMainC11(cv::Mat & hist,int n)
{
	QStringList maincolors;
	maincolors.clear();
	if(!hist.empty())
	{
		cv::Mat idx;
		cv::sortIdx(hist,idx,CV_SORT_EVERY_COLUMN+CV_SORT_DESCENDING);
		
		for(int i=0;i<n;i++)
		{
			maincolors+=getC11Name(idx.at<int>(i));	
		}
	}
	return maincolors;
}

////////////////////////////////////////////////////////////////////////////////
//Retourne le nom de la couleur C11 "n"
////////////////////////////////////////////////////////////////////////////////
QString getC11Name(int n)
{
	QStringList color_list;
	color_list<<"black"<<"gray"<<"white"<<"red"<<"orange"<<"yellow"<<"green"<<"blue"<<"purple"<<"pink"<<"brown";
	if(n>=0 && n<11)
	{	
		return color_list[n];
	}
	else
	{
		return QString("erreur");
	}
}

////////////////////////////////////////////////////////////////////////////////
//retourne la couleur correspondant a lindice C11
////////////////////////////////////////////////////////////////////////////////
QColor getC11Color(int n)
{
	unsigned char CLUT_COLORS_11[11][3]=
	{
		{0  ,0  ,0  },//noir		
		{128,128,128},//gris		
		{255,255,255},//blanc
		{255,0  ,0  },//rouge
		{255,128,0  },//orange		
		{255,255,0  },//jaune
		{0  ,255,0  },//vert
		{0  ,0  ,255},//bleu
		{150,0  ,0  },//pourpre
		{255,0  ,255},//rose 
		{128,64 ,0  }//maron 
	};

	return QColor(CLUT_COLORS_11[n][0],CLUT_COLORS_11[n][1],CLUT_COLORS_11[n][2]);
}
////////////////////////////////////////////////////////////////////////////////
//Fonction de création d'une CLUT11c par minimisation des distances
////////////////////////////////////////////////////////////////////////////////
void get_C11_CLUT(cv::Mat & clut11c,int res)
{
	unsigned char CLUT_COLORS_11[11][3]=
	{
		{0  ,0  ,0  },//noir		
		{128,128,128},//gris		
		{255,255,255},//blanc
		{255,0  ,0  },//rouge
		{255,128,0  },//orange		
		{255,255,0  },//jaune
		{0  ,255,0  },//vert
		{0  ,0  ,255},//bleu
		{150,0  ,0  },//pourpre
		{255,0  ,255},//rose 
		{128,64 ,0  }//maron 
	};

	clut11c.create(res*res*res,1,CV_8UC1);	
	clut11c.setTo(0);
	int res2=res*res;
	int step=256/res;
	for(int R=0,r=0;R<res;R++,r+=step)
	{
		for(int G=0,g=0;G<res;G++,g+=step)
		{
			for(int B=0,b=0;B<res;B++,b+=step)
			{
				unsigned char n=0;
				int dist_min=abs(r-CLUT_COLORS_11[0][0])+abs(g-CLUT_COLORS_11[0][1])+abs(b-CLUT_COLORS_11[0][2]);
				for(int i=1;i<11;i++)
				{
					int dist=abs(r-CLUT_COLORS_11[i][0])+abs(g-CLUT_COLORS_11[i][1])+abs(b-CLUT_COLORS_11[i][2]);
					if(dist_min>dist)
					{
						dist_min=dist;
						n=i;
					}
				}
				if(n<11)
				{
					clut11c.data[res2*R+res*G+B]=n;
				}
				else
				{
					printf("Erreur :%d\n",n);
				}
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//Fonctions de gestion d'une conversion C16 a partir d'une CLUT C11
////////////////////////////////////////////////////////////////////////////////
void RGB_2_C16_fromC11(cv::Mat & src, cv::Mat & clut11c, cv::Mat & des)
{
	if(src.depth()==CV_8U && src.channels()==3)
	{
		des.create(src.rows,src.cols,CV_8UC1);
		
		//on recupere la resolution de la CLUT
		int res=(int)round(pow(clut11c.rows,1.0/3.0));
		int res2=res*res;

		int np=src.rows*src.cols;
		for(int p=0;p<np;p++)
		{
			int r=floor(src.data[3*p]*res/256);
			int g=floor(src.data[3*p+1]*res/256);
			int b=floor(src.data[3*p+2]*res/256);

			int n=clut11c.data[(r*res2+g*res+b)];
			if(n>2)des.data[p]=n-3;
			else
			{
				des.data[p]=(int)floor((r+g+b)*8.0/(3.0*res))+8;
			}
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC3\n");
	}
}

void RGB_2_RGB16_fromC11(cv::Mat & src, cv::Mat & clut11c, cv::Mat & des)
{
	unsigned char CLUT_COLORS_16[16][3]=
	{
		{255,0  ,0  },//rouge
		{255,128,0  },//orange		
		{255,255,0  },//jaune
		{0  ,255,0  },//vert
		{0  ,0  ,255},//bleu
		{150,0  ,0  },//pourpre
		{255,0  ,255},//rose 
		{128,64 ,0  },//maron 
		{0  ,0  ,0  },//noir
		{36 ,36 ,36 },//noir	
		{72 ,72 ,72 },//noir
		{109,109,109},//noir
		{145,145,145},//gris		
		{182,182,182},//gris	
		{218,218,218},//gris			
		{255,255,255}//blanc
	};

	if(src.depth()==CV_8U && src.channels()==3)
	{
		des.create(src.rows,src.cols,CV_8UC3);

		//on recupere la resolution de la CLUT
		int res=(int)round(pow(clut11c.rows,1.0/3.0));
		int res2=res*res;
		
		int np=src.rows*src.cols*3;
		for(int p=0;p<np;p+=3)
		{
			int r=floor(src.data[p]*res/256);
			int g=floor(src.data[p+1]*res/256);
			int b=floor(src.data[p+2]*res/256);
			
			int n=clut11c.data[(r*res2+g*res+b)];
				
			if(n>2)n=n-3;
			else			
			{
				n=(int)floor((r+g+b)*8.0/(3.0*res))+8;
			}			

			if(n>=0 && n<16)
			{
				des.data[p]=CLUT_COLORS_16[n][0];
				des.data[p+1]=CLUT_COLORS_16[n][1];
				des.data[p+2]=CLUT_COLORS_16[n][2];
			}
			else{printf("Error : Bad input %d\n",n);break;}
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC3\n");
	}
}

////////////////////////////////////////////////////////////////////////////////
//Fonction de gestion d'une conversion C16 a partir d'une clut C16
////////////////////////////////////////////////////////////////////////////////
void C16_2_RGB(cv::Mat & src, cv::Mat & des)
{
	unsigned char CLUT_COLORS_16[16][3]=
	{
		{255,0  ,0  },//rouge
		{255,128,0  },//orange		
		{255,255,0  },//jaune
		{0  ,255,0  },//vert
		{0  ,0  ,255},//bleu
		{150,0  ,0  },//pourpre
		{255,0  ,255},//rose 
		{128,64 ,0  },//maron 
		{0  ,0  ,0  },//noir
		{36 ,36 ,36 },//noir	
		{72 ,72 ,72 },//noir
		{109,109,109},//noir
		{145,145,145},//gris		
		{182,182,182},//gris	
		{218,218,218},//gris			
		{255,255,255}//blanc
	};

	if(src.depth()==CV_8U && src.channels()==1)
	{
		des.create(src.rows,src.cols,CV_8UC3);

		int np=src.rows*src.cols;
		for(int p=0;p<np;p++)
		{
			int n=src.data[p];
			if(n>=0 && n<16){
			des.data[3*p]=CLUT_COLORS_16[n][0];
			des.data[3*p+1]=CLUT_COLORS_16[n][1];
			des.data[3*p+2]=CLUT_COLORS_16[n][2];}
			else{printf("Error : Bad input %d\n",n);break;}
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC1\n");
	}
}

void CLUTC11_2_CLUTC16(cv::Mat & clut11c, cv::Mat & clut16c)
{
	clut16c.create(clut11c.rows,1,CV_8UC1);

	//on recupere la resolution de la CLUT
	int res=(int)round(pow(clut11c.rows,1.0/3.0));
	int res2=res*res;

	for(int p=0;p<clut11c.rows;p++)
	{
		if(clut11c.data[p]>2){clut16c.data[p]=clut11c.data[p]-3;}
		else		
		{
			unsigned char R=floor(p/res2);
			unsigned char G=floor((p-R*res2)/res);
			unsigned char B=floor((p-R*res2-G*res));
			
			clut16c.data[p]=(int)floor((R+G+B)*8.0/(3.0*res))+8;
		}
	}
}

void RGB_2_C16(cv::Mat & src, cv::Mat & clut16c, cv::Mat & des)
{
	if(src.depth()==CV_8U && src.channels()==3)
	{
		des.create(src.rows,src.cols,CV_8UC1);
		
		//on recupere la resolution de la CLUT
		int res=(int)round(pow(clut16c.rows,1.0/3.0));
		int res2=res*res;

		int np=src.rows*src.cols;
		for(int p=0;p<np;p++)
		{
			int r=(int)floor(src.data[3*p]*res/256.0);
			int g=(int)floor(src.data[3*p+1]*res/256.0);
			int b=(int)floor(src.data[3*p+2]*res/256.0);

			des.data[p]=clut16c.data[(r*res2+g*res+b)];
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC3\n");
	}
}

void RGB_2_RGB16(cv::Mat & src, cv::Mat & clut16c, cv::Mat & des)
{
	unsigned char CLUT_COLORS_16[16][3]=
	{
		{255,0  ,0  },//rouge
		{255,128,0  },//orange		
		{255,255,0  },//jaune
		{0  ,255,0  },//vert
		{0  ,0  ,255},//bleu
		{150,0  ,0  },//pourpre
		{255,0  ,255},//rose 
		{128,64 ,0  },//maron 
		{0  ,0  ,0  },//noir
		{36 ,36 ,36 },//noir	
		{72 ,72 ,72 },//noir
		{109,109,109},//noir
		{145,145,145},//gris		
		{182,182,182},//gris	
		{218,218,218},//gris			
		{255,255,255}//blanc
	};

	if(src.depth()==CV_8U && src.channels()==3)
	{
		des.create(src.rows,src.cols,CV_8UC3);

		//on recupere la resolution de la CLUT
		int res=(int)round(pow(clut16c.rows,1.0/3.0));
		int res2=res*res;
		
		int np=src.rows*src.cols*3;
		for(int p=0;p<np;p+=3)
		{
			int r=(int)floor(src.data[p]*res/256.0);
			int g=(int)floor(src.data[p+1]*res/256.0);
			int b=(int)floor(src.data[p+2]*res/256.0);

			int n=clut16c.data[ r*res2+g*res+b ];

			if(n>=0 && n<16){
			des.data[p]=CLUT_COLORS_16[n][0];
			des.data[p+1]=CLUT_COLORS_16[n][1];
			des.data[p+2]=CLUT_COLORS_16[n][2];
			}else{printf("Error : Bad input %d\n",n);break;}
		}
	}
	else
	{
		printf("Error : Bad input must be CV_8UC3\n");
	}
}

////////////////////////////////////////////////////////////////////////////////
//Construit un graph des connexités de la CLUT
////////////////////////////////////////////////////////////////////////////////
void getConnexGraph(cv::Mat & clut,int dim, int threshold, QCVGraph & graph)
{
	//La matrice du graph à construire
	cv::Mat_<float> adjMat;
	adjMat.create(dim,dim);
	adjMat.setTo(0);

	//on recupere la resolution de la CLUT
	int res=(int)round(pow(clut.rows,1.0/3.0));
	int res2=res*res;

	//On balaye trois fois la clut dans chaque des directions
	//RG->fixe B->varie	
	for(int r=1;r<res;r++)
	{
		for(int g=1;g<res;g++)
		{
			for(int b=1;b<res;b++)
			{
				int N=r*res2+g*res+b;
				int M=N-1;

				//Si on detect une transition
				if(clut.data[M]!=clut.data[N])
				{
					adjMat(clut.data[M],clut.data[N])+=1;
					adjMat(clut.data[N],clut.data[M])+=1;
				}
			}
		}
	}
	//GB->fixe R->varie	
	for(int g=1;g<res;g++)
	{
		for(int b=1;b<res;b++)
		{
			for(int r=1;r<res;r++)
			{
				int N=r*res2+g*res+b;
				int M=(r-1)*res2+g*res+b;

				//Si on detect une transition
				if(clut.data[M]!=clut.data[N])
				{
					adjMat(clut.data[M],clut.data[N])+=1;
					adjMat(clut.data[N],clut.data[M])+=1;
				}
			}
		}
	}

	//BR->fixe G->varie	
	for(int b=1;b<res;b++)
	{
		for(int r=1;r<res;r++)
		{
			for(int g=1;g<res;g++)
			{
				int N=r*res2+g*res+b;
				int M=r*res2+(g-1)*res+b;

				//Si on detect une transition
				if(clut.data[M]!=clut.data[N])
				{
					adjMat(clut.data[M],clut.data[N])+=1;
					adjMat(clut.data[N],clut.data[M])+=1;
				}
			}
		}
	}
	
	//si la surface de contacte est < seuil les couleurs ne sont pas connectées
	for(int i=0;i<dim;i++)
	{
		for(int j=0;j<dim;j++)
		{
			if(i!=j)
			{
				if(adjMat(i,j)<=threshold)
				{
					adjMat(i,j)=INF;
				}
				else
				{
					adjMat(i,j)=1;
				}
			}
		}
	}

	//on charge le graph
	graph.read(adjMat);
}

////////////////////////////////////////////////////////////////////////////////
void getConnexGraph(cv::Mat & clut,int dim, int threshold, cv::Mat & adjenceMat)
{
	//La matrice du graph à construire
	cv::Mat_<float> adjMat;
	adjMat.create(dim,dim);
	adjMat.setTo(0);

	//on recupere la resolution de la CLUT
	int res=(int)round(pow(clut.rows,1.0/3.0));
	int res2=res*res;

	//On balaye trois fois la clut dans chaque des directions
	//RG->fixe B->varie	
	for(int r=1;r<res;r++)
	{
		for(int g=1;g<res;g++)
		{
			for(int b=1;b<res;b++)
			{
				int N=r*res2+g*res+b;
				int M=N-1;

				//Si on detect une transition
				if(clut.data[M]!=clut.data[N])
				{
					adjMat(clut.data[M],clut.data[N])+=1;
					adjMat(clut.data[N],clut.data[M])+=1;
				}
			}
		}
	}
	//GB->fixe R->varie	
	for(int g=1;g<res;g++)
	{
		for(int b=1;b<res;b++)
		{
			for(int r=1;r<res;r++)
			{
				int N=r*res2+g*res+b;
				int M=(r-1)*res2+g*res+b;

				//Si on detect une transition
				if(clut.data[M]!=clut.data[N])
				{
					adjMat(clut.data[M],clut.data[N])+=1;
					adjMat(clut.data[N],clut.data[M])+=1;
				}
			}
		}
	}

	//BR->fixe G->varie	
	for(int b=1;b<res;b++)
	{
		for(int r=1;r<res;r++)
		{
			for(int g=1;g<res;g++)
			{
				int N=r*res2+g*res+b;
				int M=r*res2+(g-1)*res+b;

				//Si on detect une transition
				if(clut.data[M]!=clut.data[N])
				{
					adjMat(clut.data[M],clut.data[N])+=1;
					adjMat(clut.data[N],clut.data[M])+=1;
				}
			}
		}
	}
	
	//si la surface de contacte est < seuil les couleurs ne sont pas connectées
	for(int i=0;i<dim;i++)
	{
		for(int j=0;j<dim;j++)
		{
			if(i!=j)
			{
				if(adjMat(i,j)<=threshold)
				{
					adjMat(i,j)=INF;
				}
				else
				{
					adjMat(i,j)=1;
				}
			}
		}
	}

	//on retourne la matrice d'adjence
	adjenceMat=adjMat.clone();
}
////////////////////////////////////////////////////////////////////////////////
void getConnexGraphSurfaceWeighted(cv::Mat & clut,int dim, int threshold, cv::Mat & adjenceMat)
{
	//La matrice du graph à construire
	cv::Mat_<float> adjMat;
	adjMat.create(dim,dim);
	adjMat.setTo(0);

	//on recupere la resolution de la CLUT
	int res=(int)round(pow(clut.rows,1.0/3.0));
	int res2=res*res;

	//On balaye trois fois la clut dans chaque des directions
	//RG->fixe B->varie	
	for(int r=1;r<res;r++)
	{
		for(int g=1;g<res;g++)
		{
			for(int b=1;b<res;b++)
			{
				int N=r*res2+g*res+b;
				int M=N-1;

				//Si on detect une transition
				if(clut.data[M]!=clut.data[N])
				{
					adjMat(clut.data[M],clut.data[N])+=1;
					adjMat(clut.data[N],clut.data[M])+=1;
				}
			}
		}
	}
	//GB->fixe R->varie	
	for(int g=1;g<res;g++)
	{
		for(int b=1;b<res;b++)
		{
			for(int r=1;r<res;r++)
			{
				int N=r*res2+g*res+b;
				int M=(r-1)*res2+g*res+b;

				//Si on detect une transition
				if(clut.data[M]!=clut.data[N])
				{
					adjMat(clut.data[M],clut.data[N])+=1;
					adjMat(clut.data[N],clut.data[M])+=1;
				}
			}
		}
	}

	//BR->fixe G->varie	
	for(int b=1;b<res;b++)
	{
		for(int r=1;r<res;r++)
		{
			for(int g=1;g<res;g++)
			{
				int N=r*res2+g*res+b;
				int M=r*res2+(g-1)*res+b;

				//Si on detect une transition
				if(clut.data[M]!=clut.data[N])
				{
					adjMat(clut.data[M],clut.data[N])+=1;
					adjMat(clut.data[N],clut.data[M])+=1;
				}
			}
		}
	}
	
	//si la surface de contacte est < seuil les couleurs ne sont pas connectées
	for(int i=0;i<dim;i++)
	{
		for(int j=0;j<dim;j++)
		{
			if(i!=j)
			{
				if(adjMat(i,j)<threshold)
				{
					adjMat(i,j)=INF;
				}
				else
				{
					adjMat(i,j)=1.0/adjMat(i,j);
				}
			}
		}
	}

	//on retourne la matrice d'adjence
	adjenceMat=adjMat.clone();
}
